// ==============================================================================
//
// Software Development Kit v 3.0
// loader dil-engine dll
//
// Copyright (c) 2008-2009 Gear Pair Games
//
// Copyright and license exceptions noted in source
//
// Description: loader all dil in folder dll
// ==============================================================================
unit main;

interface

uses
  cls,
  types,
  structs,
  headers,
  os,
  fileApi,
  enums;

type
// CResourceFactory
  CResourceLoader = class( CThread )
  private
  public
    procedure doExecute; override;
  end;

// CResourceFactory
  CResourceFactory = class( IResourceFactory )
  private
    fthread : CResourceLoader;
  public
    procedure doInit; override;
    procedure doFree; override;
  end;

// CFileSystem
  CFileSystem = class( IFileSystem )
    constructor Create( const inDir : ansiString );
  private
    fFiles : list;
    fDir : ansiString;
    procedure enumerate( const inFile : osFile );
    function getUniqHandle : handle;
  public
    procedure enum( const inName : ansiString; const inProc : enumProcFile );
    function getFileHandle( const inName : pchar ) : handle; override;
    function getfile( const inHNDL : handle ) : IFileStream; override;
  public
    procedure doInit; override;
    procedure doFree; override;
  end;

// CMain
  CMain = class( INotifier )
    constructor Create;
    destructor Destroy; override;
  private
    fPath : ansiString;
    fLog : CTextFile;
    fRun : bool;
    fhooks : list;
    fTimer : CTimer;
    gameTime : double;
// CMain::enumerate
    procedure enumerate( const inFile : osFile );
// CMain::doTick
    function doTick : bool;
  protected
// CMain::getInput
    function getInput : IInputApi; override;
// CMain::getRender
    function getRenderer : IRenderableApi; override;
// CMain::getFileSystem
    function getFileSystem : IFileSystem; override;
  public { public }
// CMain::loaddil
    procedure loaddil;
// CMain::writeLog
    procedure writeLog( const inMsgType : enumLogMsgType; const inMsg : pchar; const inFlagMode : enumOfLogMode = [ ] ); override;
// CMain::getPath
    function getPath : pchar; override;
// CMain::run
    procedure run;
// CMain::acceptMsg
    function acceptMsg( var inHandle, inMsg : uint; var wparam, lparam : int ) : int;
// CMain::acceptMsg
    procedure appexit;
// CMain::regOsCallHook
    procedure regOsCallHook( const inCall : funcObjOsHookMsg ); override;
// CMain::sendNotifierMsg
    procedure sendNotifierMsg( const inMsg : pstructMsg ); override;
  public
// CMain::doInit
    procedure doInit; override;
// CMain::doFree
    procedure doFree; override;
  end;

implementation

uses
  utils,
  msg,
  html,
  consts,
  math;
{ CMain }

// CMain::Create
procedure CMain.appexit;
begin
  boolFalse( fRun );
end;

// CMain::Create
constructor CMain.Create;
const
  stdRealPath = nullInt;
begin
  fRun := false;
  fPath := osGetDir( stdRealPath );
  fLog := CTextFile.Create;
  fhooks := list.Create( 0, SizeOf( TMethod ) );
// log open file
  exception( fLog.Open( fPath + msgFileLog, [ fmCreate ] ), msgErrCouldNotOpenFile + msgFileLog );
// log html header write
  writeLog( logNone, htmlLogHeader );
  fTimer := CTimer.Create( 0.01 );
  inherited;
end;

// CMain::acceptMsg
function CMain.acceptMsg( var inHandle, inMsg : uint; var wparam, lparam : int ) : int;
var
  i : int;
const
  codeExit = -1;
  codeDone = 0;
begin
  for i := 0 to fhooks.count - 1 do
  begin
    result := funcObjOsHookMsg( fhooks[ i ]^ )( inHandle, inMsg, wparam, lparam );
    if result = codeExit then
     exit;
  end;
// exit by done
  result := codeDone;
end;

// CMain::Destroy
destructor CMain.Destroy;
begin
// close html tag in log
  writeLog( logNone, htmlLogHeaderEnd );
  fLog.close; // log has been closed
  inherited;
end;

// CMain::doFree
procedure CMain.doFree;
begin
// free hooks list
  fhooks.free( true );
  fTimer.free;
  inherited;
end;

procedure CMain.doInit;
begin
// init file system
  addChild( CFileSystem.Create( fPath ) );
// init resource manager
  addChild( CResourceFactory.Create );
  inherited;
end;

// CMain::doTick
const
  MaxDT = 10;

const
  cDeltaTime = 1 / 50;

function CMain.doTick : bool;
begin
  fTimer.calc;
  while gameTime < fTimer.gettime do
  begin
    gameTime := gameTime + cDeltaTime;
     // doUpdate&doDraw
    doUpdate;
  end;
// result
  result := fRun;
end;

// CMain::enumerate
procedure CMain.enumerate( const inFile : osFile );
var
  lib : handle;
  stdEntry : function( const inNotifier : CObject ) : CObject;
cdecl;
begin
  lib := osLibLoad( inFile.realtiveDir + inFile.fileName );
// check on exception
  exception( lib <> 0, msgErrUnableLoadDll + inFile.fileName );
// enrty   lib and call init-function
  stdEntry := osGetProc( lib, msgDllEntryName );
// check on exception
  exception( @stdEntry <> nil, msgErrCanNotEnterDll + inFile.fileName );
// call init  *p
// registry dll'class and init
  addChild( stdEntry( Self ) );
end;

// CMain::getFileSystem
function CMain.getFileSystem : IFileSystem;
const
  intf : IFileSystem = nil;
begin
  if intf = nil then
    PCObject( @intf )^ := getChildByMeta( IFileSystem );
  result := intf;
end;

// CMain::getInput
function CMain.getInput : IInputApi;
const
  intf : IInputApi = nil;
begin
  if intf = nil then
    PCObject( @intf )^ := getChildByMeta( IInputApi );
  result := intf;
end;

// CMain::getPath
function CMain.getPath : pchar;
begin
  result := pchar( fPath );
end;

// CMain::getRenderer
function CMain.getRenderer : IRenderableApi;
const
  intf : IRenderableApi = nil;
begin
  if intf = nil then
    PCObject( @intf )^ := getChildByMeta( IRenderableApi );
  result := intf;
end;

// CMain::loaddil
procedure CMain.loaddil;
begin
  osEnumFiles( fPath + 'dll\', '*' + extDll, false, enumerate );
end;

// CMain::run
procedure CMain.run;
begin
  boolTrue( fRun );
// copy start time
  fTimer.calc;
  gameTime := fTimer.gettime;
// loop!!
  printf( logDebug, 'entry to main loop' );
  osMainLoop( doTick, acceptMsg );
  printf( logDebug, 'exit main loop' );
end;

procedure CMain.sendNotifierMsg( const inMsg : pstructMsg );
begin
  if inMsg <> nil then
    doMessage( inMsg^ );
end;

// CMain::regOsCallHook
procedure CMain.regOsCallHook( const inCall : funcObjOsHookMsg );
var
  proc : ^TMethod;
begin
  new( proc );
  proc^ := TMethod( inCall );
  fhooks.add( proc );
end;

// CMain::writeLog
procedure CMain.writeLog( const inMsgType : enumLogMsgType; const inMsg : pchar; const inFlagMode : enumOfLogMode = [ ] );
const
  htmlPrefix : array [ enumLogMsgType ] of ansiString = ( '', 'class="err">error:</td>' + #13 + '<td class="err">',
    'class="info">info:</td>' + #13 + '<td class="info">', 'class="warn">warn:</td>' + #13 + '<td class="warn">',
    'class="log">log:</td>' + #13 + '<td class="log">', 'class="log">dbg:</td>' + #13 + '<td class="debug">' );
  // msgError, msgHint, msgWarn, msgLog
var
  lgStr : ansiString;
begin
// low case
  if ( logLowcase in inFlagMode ) then
    strSetlowcase( inMsg );
//
  if ( inMsgType = logNone ) then
    fLog.writeText( inMsg )
  else
  begin
    lgStr := inMsg;
    lgStr := htmlLogBegin + htmlPrefix[ inMsgType ] + lgStr + htmlLogEnd;
    fLog.writeText( lgStr );
  end;

  if inMsgType = logError then
    exception( false, inMsg );
end;

{ CFileSystem }

// CFileSystem::constructor
constructor CFileSystem.Create( const inDir : ansiString );
begin
  inherited Create;
  fDir := inDir;
end;

// CFileSystem::enumerate
procedure CFileSystem.doFree;
var
  i : int;
begin
// free strings
  for i := 0 to fFiles.count - 1 do
    dispose( posFile( fFiles[ i ] ) );
// free list
  fFiles.free( false );
  inherited;
end;

procedure CFileSystem.doInit;
const
  res_dir : ansiString = 'resource\';
begin
  fFiles := list.Create( 0, SizeOf( osFile ) );
  osEnumFiles( fDir + res_dir, '*.*', true, enumerate );
  inherited;
end;

procedure CFileSystem.enum( const inName : ansiString; const inProc : enumProcFile );
begin
// todo: insert code
end;

procedure CFileSystem.enumerate( const inFile : osFile );
var
  p : ^osFile;
begin
  new( p );
  p^ := inFile;
  p^.hndl := getUniqHandle;
  fFiles.add( p );
// printf(logDebug, inFile.fileName);
end;

function CFileSystem.getfile( const inHNDL : handle ) : IFileStream;
var
  j : int;
begin
  result := nil;
// loop
  for j := 0 to fFiles.count - 1 do
    with posFile( fFiles[ j ] )^ do
     if hndl = inHNDL then
     begin
     result := CFileStream.Create;
     if not CFileStream( result ).Open( realtiveDir + fileName ) then
     begin
     result.free;
     result := nil;
     end;
     exit;
     end;
end;

function CFileSystem.getFileHandle( const inName : pchar ) : handle;
var
  j : int;
  tmp : ansiString;
begin
  tmp := inName;
  result := nullInt;
// loop
  for j := 0 to fFiles.count - 1 do
    with posFile( fFiles[ j ] )^ do
     if strCmp( tmp, fileName, false ) then
     begin
     result := hndl;
     exit;
     end;
end;

function CFileSystem.getUniqHandle : handle;
var
  j : int;
begin
  result := oneInt;
// loop
  for j := 0 to fFiles.count - 1 do
    if result <> posFile( fFiles[ j ] ).hndl then
     exit
    else
     inc( result );
end;

{ CResourceFactory }

procedure CResourceFactory.doFree;
begin
// exit!
  fthread.waitAndExit;
  inherited;
end;

procedure CResourceFactory.doInit;
begin
  inherited;
// init thread
  fthread := CResourceLoader.Create( thrdNormal, Self );
end;

{ CResourceLoader }

procedure CResourceLoader.doExecute;
begin
// thread loop
  while not exit do
    ;
// call main proc
  inherited;
end;

end.
